/***********************************************************************************/
/* Code license:  GNU General Public License v3                                    */
/* Main page:     https://code.google.com/p/library-cross-platform/                */
/* Issue tracker: https://code.google.com/p/library-cross-platform/issues/list     */
/***********************************************************************************/

#ifndef __CPP_FACTORY_15_06_2012__
#define __CPP_FACTORY_15_06_2012__


#if defined WINDOWS
// BUG: C4786 Warning Is Not Disabled with #pragma Warning
// STATUS: Microsoft has confirmed this to be a bug in the Microsoft product.
// This warning can be ignored. This occured only in the <map> container.
#pragma warning (disable : 4786 4787)
#endif

#include <map>
#include <string>
#include "types.h"


typedef struct __class_allocate_info_tag__
{
  size_t      class_size;
  std::string class_name;
  void        *pclass;
} class_allocate_info;

class ClassFactory
{
public:
  ClassFactory() {};

  virtual ~ClassFactory() {
  };

  template <class _class>
  void Register(const std::string& name) {
     int class_size = sizeof(_class);
     class_allocate_info info;

     info.class_size = class_size;
     info.class_name = name;
     info.pclass     = null;

     class_array[name] = info;
   };

  template <class baseclass>
  baseclass *getSingelton(const std::string& name) {
    baseclass *preturn = null;
    if(class_array.find(name) != class_array.end()) {
      class_allocate_info info = class_array[name];
      if(info.pclass == null) {
        void *pvoid = malloc(info.class_size);
        preturn =  new (pvoid) baseclass();
        info.pclass = pvoid;
        class_array[name] = info;
      } else {
        preturn = static_cast<baseclass*>(info.pclass);
      }
    }
    return preturn;
  };

  template <class baseclass>
  baseclass *getObject(const std::string& name) {
    baseclass *preturn = null;
    if(class_array.find(name) != class_array.end()) {
      class_allocate_info info = class_array[name];
      void *pvoid = malloc(info.class_size);
      preturn =  new (pvoid) baseclass();
    }
    return preturn;
  };

  template <class baseclass>
  baseclass *findObject(const std::string& name) {
    baseclass *preturn = null;
    if(class_array.find(name) != class_array.end()) {
      class_allocate_info info = class_array[name];
      preturn = static_cast<baseclass*>(info.pclass);
    }
    return preturn;
  };


  template <class baseclass>
  void deRegitrateObject(const std::string& name) {
    if(class_array.find(name) != class_array.end()) {
      class_allocate_info info = class_array[name];
      deleteClass<baseclass>(static_cast<baseclass*>(info.pclass));
      class_array.erase(name);
    }
  }

  protected:
    template <class _class>
    _class *getNewInstance() {
      void *pvoid = malloc(sizeof(_class));
      return new (pvoid) _class();
    }

    template <class baseclass>
    void deleteClass(baseclass *pclass) {
      pclass->~baseclass();
      delete pclass;
    }


private:
  std::map<std::string, class_allocate_info> class_array;
};


#endif
